#include "iec101device.h"

#include "phylink.h"

#include "iot_debug.h"
#include "tools/auto_guard.h"
#include "util_tools.h"
#include "json_util.h"
#include <logger.h>

IEC101Device::IEC101Device(uint16_t bay_id, uint16_t dev_id, IPhyLink *phy_dev) :
    IDevice (bay_id, dev_id, phy_dev)
{

}

IEC101Device::~IEC101Device()
{

}

int IEC101Device::genDevice(cJSON *root, cJSON *model_root, const char *model_name,
                             uint64_t dev_addr, cJSON */*arr_params*/)
{
    int ret;

    if (model_root == nullptr ) {
        log_info("param error\n");
        return PARSE_MODE_FAIL;
    }

    dev_info_.model = model_name;
    dev_info_.addr  = u32toa(dev_addr&0xFF);
    dev_info_.dev_id = u32toa(dev_id_);
    dev_info_.protocolName = "IEC-101";
    parseDevInfo(root);

    this->model_name_ = model_name;
    this->dev_addr_   = dev_addr&0xFFFF;

    this->sip_regs_.clear();
    ret = parseRegGroups(model_root, "SIP", sip_regs_);
    if (ret != true) {
        log_info("parse SIP failed\n");
        goto error;
    }

    this->dip_regs_.clear();
    ret = parseRegGroups(model_root, "DIP", dip_regs_);
    if (ret != true) {
        log_info("parse DIP failed\n");
        goto error;
    }

    this->measure_regs_.clear();
    ret = parseRegGroups(model_root, "Measure", measure_regs_);
    if (ret != true) {
        log_info("parse Measure failed\n");
        goto error;
    }

    this->setDevAndId(this->phy_dev_, dev_addr_);

    return true;
error:
    return ret;
}

bool IEC101Device::isActived() const
{
    return this->linked();
}

const RegBase *IEC101Device::getYX(const char *reg_name)
{
    RegBase * reg = findReg(sip_regs_, reg_name);
    if (reg == nullptr)
        reg = findReg(dip_regs_, reg_name);
    return reg;
}

bool IEC101Device::hasYX(const char *reg_name)
{
    RegBase * reg = findReg(sip_regs_, reg_name);
    if (reg == nullptr)
        reg = findReg(dip_regs_, reg_name);
    return reg!=nullptr ? true : false;
}


const RegBase *IEC101Device::getYC(const char *reg_name)
{
    return findReg(measure_regs_, reg_name);
}

bool IEC101Device::hasYC(const char *reg_name)
{
    return findReg(measure_regs_, reg_name)!=nullptr ? true : false;
}

int IEC101Device::updateReg()
{
    if (!this->linked())
    {
        if (!this->linkDevice())
        {
            this->clearRegs();
            return ERR_RECV_TIMEOUT;
        }
    }

    this->requestData();

    if (!this->linked())
    {
        this->clearRegs();
    }

    return this->linked() ? (ERR_NO_ERROR) : (ERR_RECV_TIMEOUT);
}


int IEC101Device::actions()
{
    return 0;
}

const char *IEC101Device::phyLinkDeviceName() const
{
    if (phy_dev_) {
        return phy_dev_->devName();
    }
    return nullptr;
}

void IEC101Device::dumpYX()
{
    AUTO_GUARD(gd, MMutex, yx_mtx_);
    dumpData(sip_regs_);
    dumpData(dip_regs_);
}

void IEC101Device::dumpYC()
{
    AUTO_GUARD(gd, MMutex, measure_mtx_);
    dumpData(measure_regs_);
}

void IEC101Device::clearRegs()
{
    measure_mtx_.lock();
    for (RegGroups::const_iterator it = measure_regs_.begin(); it != measure_regs_.end(); it++)
    {
        for (RegInfos::const_iterator inf = it->infos.begin(); inf != it->infos.end(); inf++)
        {
            RegBase *reg = *inf;
            reg->setStatus(RegBase::INVALID);
        }
    }
    measure_mtx_.unlock();

    yx_mtx_.lock();
    for (RegGroups::const_iterator it=sip_regs_.begin(); it!= sip_regs_.end(); it++)
    {
        for (RegInfos::const_iterator inf = it->infos.begin(); inf != it->infos.end(); inf++)
        {
            RegBase *reg = *inf;
            reg->setStatus(RegBase::INVALID);
        }
    }
    for (RegGroups::const_iterator it=dip_regs_.begin(); it!= dip_regs_.end(); it++)
    {
        for (RegInfos::const_iterator inf = it->infos.begin(); inf != it->infos.end(); inf++)
        {
            RegBase *reg = *inf;
            reg->setStatus(RegBase::INVALID);
        }
    }
    yx_mtx_.unlock();
}

void IEC101Device::getAllRegs(IDevice::Datas &regs) const
{
    regs.clear();

    for (RegGroups::const_iterator it=sip_regs_.begin(); it!= sip_regs_.end(); it++) {
        for (RegInfos::const_iterator inf=it->infos.begin(); inf!=it->infos.end(); inf++) {
            regs.push_back(*inf);
        }
    }
    for (RegGroups::const_iterator it=dip_regs_.begin(); it!= dip_regs_.end(); it++) {
        for (RegInfos::const_iterator inf=it->infos.begin(); inf!=it->infos.end(); inf++) {
            regs.push_back(*inf);
        }
    }
}

void IEC101Device::getYX(IDevice::Datas &datas, bool force)
{
    datas.clear();

    yx_mtx_.lock();
    for (RegGroups::const_iterator it=sip_regs_.begin(); it!= sip_regs_.end(); it++) {
        for (RegInfos::const_iterator inf=it->infos.begin(); inf!=it->infos.end(); inf++) {
            RegBase *reg = *inf;
            if (reg->changed(force))
                datas.push_back(reg);
        }
    }
    yx_mtx_.unlock();


    dip_mtx_.lock();
    for (RegGroups::const_iterator it=dip_regs_.begin(); it!= dip_regs_.end(); it++) {
        for (RegInfos::const_iterator inf=it->infos.begin(); inf!=it->infos.end(); inf++) {
            RegBase *reg = *inf;
            if (reg->changed(force))
                datas.push_back(reg);
        }
    }
    dip_mtx_.unlock();
}

void IEC101Device::getYC(IDevice::Datas &datas, bool force)
{
    datas.clear();
    AUTO_GUARD(gd, MMutex, measure_mtx_);
    for (RegGroups::const_iterator it=measure_regs_.begin(); it!= measure_regs_.end(); it++) {
        for (RegInfos::const_iterator inf=it->infos.begin(); inf!=it->infos.end(); inf++) {
            RegBase *reg = *inf;
            if (reg->changed(force))
                datas.push_back(reg);
        }
    }
}

void IEC101Device::OnLinkLost()
{
    log_info("dev(%s) 连接断开(端口号:%s) ----\n", this->devName().c_str(), phy_dev_->UartName());
}

void IEC101Device::OnSetSIP(uint16_t addr, IEC101Impl::SinglePointStatus sp)
{
    RegGroups::iterator it = sip_regs_.begin();
    for (; it!=sip_regs_.end(); it++) {
        if (it->obj.addr <= addr) {
            uint16_t offset = addr-it->obj.addr;
            if (offset < it->obj.num) {
                RegInfos::iterator it_r = it->infos.begin();
                while (offset--) {
                    it_r++;
                }
                AUTO_GUARD(gd, MMutex, yx_mtx_);
                (*it_r)->setRawData(sp == SP_ON ? true : false);
                (*it_r)->setStatus(RegBase::VALID);
                break;
            }
        }
    }
}
void IEC101Device::OnSetDIP(uint16_t addr, IEC101Impl::DoublePointStatus dp)
{
    RegGroups::iterator it = dip_regs_.begin();
    for (; it!=dip_regs_.end(); it++) {
        if (it->obj.addr <= addr) {
            uint16_t offset = addr-it->obj.addr;
            if (offset < it->obj.num) {
                RegInfos::iterator it_r = it->infos.begin();
                while (offset--) {
                    it_r++;
                }
                AUTO_GUARD(gd, MMutex, yx_mtx_);
                (*it_r)->setRawData(dp == DP_ON ? true : false);
                (*it_r)->setStatus(RegBase::VALID);
                break;
            }
        }
    }
}

void IEC101Device::OnSetMeasure(uint16_t addr, float v)
{
    RegGroups::iterator it = measure_regs_.begin();
    for (; it!=measure_regs_.end(); it++) {
        if (it->obj.addr <= addr) {
            uint16_t offset = addr-it->obj.addr;
            if (offset < it->obj.num) {
                RegInfos::iterator it_r = it->infos.begin();
                while (offset--) {
                    it_r++;
                }
                AUTO_GUARD(gd, MMutex, measure_mtx_);
                (*it_r)->setRawData(v);
                (*it_r)->setStatus(RegBase::VALID);
                break;
            }
        }
    }
}


bool IEC101Device::parseInfoObjReg(cJSON *root, IEC101Device::InfoObjReg &obj)
{
    if (!cJSON_GetInt(root, "infoObjAddr", obj.addr)) {
        log_info("parse infoObjAddr failed\n");
        return false;
    }
    if (!cJSON_GetInt(root, "infoObjNum", obj.num)) {
        log_info("parse infoObjNum failed\n");
        return false;
    }
    return true;
}

int IEC101Device::parseRegGroup(cJSON *root, RegGroup &group, bool /*is_yc*/)
{
    int ret = PARSE_MODE_FAIL;

    if (!parseInfoObjReg(root, group.obj))
        goto error;

    group.infos.clear();
    ret = ::parseRegs(root, group.infos);
    if (ret != true)
        goto error;

    if (group.obj.num != group.infos.size()) {
        log_info("Regs.size(%d) is over RegNum(%d)\n", group.infos.size(), group.obj.num);
        ret = MODE_REGSIZE_ERROR;
        goto error;
    }

    return true;
error:
    log_info("parse failed\n");
    return ret;
}

int IEC101Device::parseRegGroups(cJSON *root, const char *block_name, RegGroups &groups)
{
    int ret = PARSE_MODE_FAIL;

    cJSON *js = cJSON_GetObjectItem(root, block_name);
    if (!js) return true;

    if (js->type == cJSON_Array) {
        int item = 0;
        do {
            cJSON* arrItem = cJSON_GetArrayItem(js, item);
            if (arrItem) {
                RegGroup gruop;

                ret = parseRegGroup(arrItem, gruop, strcmp(block_name, "Measure")==0);
                if (ret == true) {
                    groups.push_back(gruop);
                }
                else {
                    goto error;
                }
                item++;
            }
            else
                break;
        } while(true);
    }

    return true;
error:
    log_info("parse failed\n");
    return ret;
}

void IEC101Device::dumpData(const IEC101Device::RegGroups &regs)
{
    bool print = false;

    for (RegGroups::const_iterator it=regs.begin(); it!= regs.end(); it++)
    {
        dumpRegData(it->infos);
        print = true;
    }

    if (print)
        printf("\n");
}

RegBase *IEC101Device::findReg(IEC101Device::RegGroups &regs, const std::string &name)
{
    for (RegGroups::iterator it=regs.begin(); it!= regs.end(); it++) {
        RegBase *reg = ::findReg(it->infos, name);
        if (reg)
            return reg;
    }
    return nullptr;
}
